/*
* Copyright (C) 1997-2001 Id Software, Inc.
* 
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
* 
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE.
* 
* See the GNU General Public License for more details.
* 
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place - Suite 330, Boston, MA 02111-1307, USA.
*  
*/

// Created on 17.12.2003 by RST.
// $Id: Monster.java,v 1.7 2005/11/20 22:18:33 salomo Exp $
using System;
using Defines = Suake2.UI.Defines;
using M = Suake2.UI.client.M;
using CM = Suake2.UI.qcommon.CM;
using Com = Suake2.UI.qcommon.Com;
using Suake2.UI.util;
namespace Suake2.UI.game
{
	
	public class Monster
	{
		public class AnonymousClassEntThinkAdapter:EntThinkAdapter
		{
			override public System.String ID
			{
				get
				{
					return "monster_think";
				}
				
			}
			public override bool think(edict_t self)
			{
				
				M.M_MoveFrame(self);
				if (self.linkcount != self.monsterinfo.linkcount)
				{
					self.monsterinfo.linkcount = self.linkcount;
					M.M_CheckGround(self);
				}
				M.M_CatagorizePosition(self);
				M.M_WorldEffects(self);
				M.M_SetEffects(self);
				return true;
			}
		}
		public class AnonymousClassEntThinkAdapter1:EntThinkAdapter
		{
			override public System.String ID
			{
				get
				{
					return "monster_trigger_spawn";
				}
				
			}
			public override bool think(edict_t self)
			{
				
				self.s.origin[2] += 1;
				GameUtil.KillBox(self);
				
				self.solid = Defines.SOLID_BBOX;
				self.movetype = Defines.MOVETYPE_STEP;
				self.svflags &= ~ Defines.SVF_NOCLIENT;
				self.air_finished = GameBase.level.time + 12;
				GameBase.gi.linkentity(self);
				
				Monster.monster_start_go(self);
				
				if (self.enemy != null && 0 == (self.spawnflags & 1) && 0 == (self.enemy.flags & Defines.FL_NOTARGET))
				{
					GameUtil.FoundTarget(self);
				}
				else
				{
					self.enemy = null;
				}
				return true;
			}
		}
		public class AnonymousClassEntUseAdapter:EntUseAdapter
		{
			override public System.String ID
			{
				get
				{
					return "monster_trigger_spawn_use";
				}
				
			}
			public override void  use(edict_t self, edict_t other, edict_t activator)
			{
				self.think = Suake2.UI.game.Monster.monster_triggered_spawn;
				self.nextthink = GameBase.level.time + Defines.FRAMETIME;
				if (activator.client != null)
					self.enemy = activator;
				self.use = GameUtil.monster_use;
			}
		}
		public class AnonymousClassEntThinkAdapter2:EntThinkAdapter
		{
			override public System.String ID
			{
				get
				{
					return "monster_triggered_start";
				}
				
			}
			public override bool think(edict_t self)
			{
				if (self.index == 312)
					Com.Printf("monster_triggered_start\n");
				self.solid = Defines.SOLID_NOT;
				self.movetype = Defines.MOVETYPE_NONE;
				self.svflags |= Defines.SVF_NOCLIENT;
				self.nextthink = 0;
				self.use = Suake2.UI.game.Monster.monster_triggered_spawn_use;
				return true;
			}
		}
		
		// FIXME monsters should call these with a totally accurate direction
		//	and we can mess it up based on skill. Spread should be for normal
		//	and we can tighten or loosen based on skill. We could muck with
		//	the damages too, but I'm not sure that's such a good idea.
		public static void  monster_fire_bullet(edict_t self, float[] start, float[] dir, int damage, int kick, int hspread, int vspread, int flashtype)
		{
			GameWeapon.fire_bullet(self, start, dir, damage, kick, hspread, vspread, Defines.MOD_UNKNOWN);
			
			GameBase.gi.WriteByte(Defines.svc_muzzleflash2);
			GameBase.gi.WriteShort(self.index);
			GameBase.gi.WriteByte(flashtype);
			GameBase.gi.multicast(start, Defines.MULTICAST_PVS);
		}
		
		/// <summary>The Moster fires the shotgun. </summary>
		public static void  monster_fire_shotgun(edict_t self, float[] start, float[] aimdir, int damage, int kick, int hspread, int vspread, int count, int flashtype)
		{
			GameWeapon.fire_shotgun(self, start, aimdir, damage, kick, hspread, vspread, count, Defines.MOD_UNKNOWN);
			
			GameBase.gi.WriteByte(Defines.svc_muzzleflash2);
			GameBase.gi.WriteShort(self.index);
			GameBase.gi.WriteByte(flashtype);
			GameBase.gi.multicast(start, Defines.MULTICAST_PVS);
		}
		
		/// <summary>The Moster fires the blaster. </summary>
		public static void  monster_fire_blaster(edict_t self, float[] start, float[] dir, int damage, int speed, int flashtype, int effect)
		{
			GameWeapon.fire_blaster(self, start, dir, damage, speed, effect, false);
			
			GameBase.gi.WriteByte(Defines.svc_muzzleflash2);
			GameBase.gi.WriteShort(self.index);
			GameBase.gi.WriteByte(flashtype);
			GameBase.gi.multicast(start, Defines.MULTICAST_PVS);
		}
		
		/// <summary>The Moster fires the grenade. </summary>
		public static void  monster_fire_grenade(edict_t self, float[] start, float[] aimdir, int damage, int speed, int flashtype)
		{
			GameWeapon.fire_grenade(self, start, aimdir, damage, speed, 2.5f, damage + 40);
			
			GameBase.gi.WriteByte(Defines.svc_muzzleflash2);
			GameBase.gi.WriteShort(self.index);
			GameBase.gi.WriteByte(flashtype);
			GameBase.gi.multicast(start, Defines.MULTICAST_PVS);
		}
		
		/// <summary>The Moster fires the rocket. </summary>
		public static void  monster_fire_rocket(edict_t self, float[] start, float[] dir, int damage, int speed, int flashtype)
		{
			GameWeapon.fire_rocket(self, start, dir, damage, speed, damage + 20, damage);
			
			GameBase.gi.WriteByte(Defines.svc_muzzleflash2);
			GameBase.gi.WriteShort(self.index);
			GameBase.gi.WriteByte(flashtype);
			GameBase.gi.multicast(start, Defines.MULTICAST_PVS);
		}
		
		/// <summary>The Moster fires the railgun. </summary>
		public static void  monster_fire_railgun(edict_t self, float[] start, float[] aimdir, int damage, int kick, int flashtype)
		{
			GameWeapon.fire_rail(self, start, aimdir, damage, kick);
			
			GameBase.gi.WriteByte(Defines.svc_muzzleflash2);
			GameBase.gi.WriteShort(self.index);
			GameBase.gi.WriteByte(flashtype);
			GameBase.gi.multicast(start, Defines.MULTICAST_PVS);
		}
		
		/// <summary>The Moster fires the bfg. </summary>
		public static void  monster_fire_bfg(edict_t self, float[] start, float[] aimdir, int damage, int speed, int kick, float damage_radius, int flashtype)
		{
			GameWeapon.fire_bfg(self, start, aimdir, damage, speed, damage_radius);
			
			GameBase.gi.WriteByte(Defines.svc_muzzleflash2);
			GameBase.gi.WriteShort(self.index);
			GameBase.gi.WriteByte(flashtype);
			GameBase.gi.multicast(start, Defines.MULTICAST_PVS);
		}
		
		/*
		* ================ monster_death_use
		* 
		* When a monster dies, it fires all of its targets with the current enemy
		* as activator. ================
		*/
		public static void  monster_death_use(edict_t self)
		{
			self.flags &= ~ (Defines.FL_FLY | Defines.FL_SWIM);
			self.monsterinfo.aiflags &= Defines.AI_GOOD_GUY;
			
			if (self.item != null)
			{
				GameItems.Drop_Item(self, self.item);
				self.item = null;
			}
			
			if (self.deathtarget != null)
				self.target = self.deathtarget;
			
			if (self.target == null)
				return ;
			
			GameUtil.G_UseTargets(self, self.enemy);
		}
		
		// ============================================================================
		public static bool monster_start(edict_t self)
		{
			if (GameBase.deathmatch.value_Renamed != 0)
			{
				GameUtil.G_FreeEdict(self);
				return false;
			}
			
			if ((self.spawnflags & 4) != 0 && 0 == (self.monsterinfo.aiflags & Defines.AI_GOOD_GUY))
			{
				self.spawnflags &= ~ 4;
				self.spawnflags |= 1;
				//		 gi.dprintf("fixed spawnflags on %s at %s\n", self.classname,
				// vtos(self.s.origin));
			}
			
			if (0 == (self.monsterinfo.aiflags & Defines.AI_GOOD_GUY))
				GameBase.level.total_monsters++;
			
			self.nextthink = GameBase.level.time + Defines.FRAMETIME;
			self.svflags |= Defines.SVF_MONSTER;
			self.s.renderfx |= Defines.RF_FRAMELERP;
			self.takedamage = Defines.DAMAGE_AIM;
			self.air_finished = GameBase.level.time + 12;
			self.use = GameUtil.monster_use;
			self.max_health = self.health;
			self.clipmask = Defines.MASK_MONSTERSOLID;
			
			self.s.skinnum = 0;
			self.deadflag = Defines.DEAD_NO;
			self.svflags &= ~ Defines.SVF_DEADMONSTER;
			
			if (null == self.monsterinfo.checkattack)
				self.monsterinfo.checkattack = GameUtil.M_CheckAttack;
			Math3D.VectorCopy(self.s.origin, self.s.old_origin);
			
			if (GameBase.st.item != null && GameBase.st.item.Length > 0)
			{
				self.item = GameItems.FindItemByClassname(GameBase.st.item);
				if (self.item == null)
					GameBase.gi.dprintf("monster_start:" + self.classname + " at " + Lib.vtos(self.s.origin) + " has bad item: " + GameBase.st.item + "\n");
			}
			
			// randomize what frame they start on
			if (self.monsterinfo.currentmove != null)
				self.s.frame = self.monsterinfo.currentmove.firstframe + (Lib.rand() % (self.monsterinfo.currentmove.lastframe - self.monsterinfo.currentmove.firstframe + 1));
			
			return true;
		}
		
		public static void  monster_start_go(edict_t self)
		{
			
			float[] v = new float[]{0, 0, 0};
			
			if (self.health <= 0)
				return ;
			
			// check for target to combat_point and change to combattarget
			if (self.target != null)
			{
				bool notcombat;
				bool fixup;
				edict_t target = null;
				notcombat = false;
				fixup = false;
				/*
				* if (true) { Com.Printf("all entities:\n");
				* 
				* for (int n = 0; n < Game.globals.num_edicts; n++) { edict_t ent =
				* GameBase.g_edicts[n]; Com.Printf( "|%4i | %25s
				* |%8.2f|%8.2f|%8.2f||%8.2f|%8.2f|%8.2f||%8.2f|%8.2f|%8.2f|\n", new
				* Vargs().add(n).add(ent.classname).
				* add(ent.s.origin[0]).add(ent.s.origin[1]).add(ent.s.origin[2])
				* .add(ent.mins[0]).add(ent.mins[1]).add(ent.mins[2])
				* .add(ent.maxs[0]).add(ent.maxs[1]).add(ent.maxs[2])); }
				* sleep(10); }
				*/
				
				EdictIterator edit = null;
				
				while ((edit = GameBase.G_Find(edit, GameBase.findByTarget, self.target)) != null)
				{
					target = edit.o;
					if (Lib.strcmp(target.classname, "point_combat") == 0)
					{
						self.combattarget = self.target;
						fixup = true;
					}
					else
					{
						notcombat = true;
					}
				}
				if (notcombat && self.combattarget != null)
					GameBase.gi.dprintf(self.classname + " at " + Lib.vtos(self.s.origin) + " has target with mixed types\n");
				if (fixup)
					self.target = null;
			}
			
			// validate combattarget
			if (self.combattarget != null)
			{
				edict_t target = null;
				
				EdictIterator edit = null;
				while ((edit = GameBase.G_Find(edit, GameBase.findByTarget, self.combattarget)) != null)
				{
					target = edit.o;
					
					if (Lib.strcmp(target.classname, "point_combat") != 0)
					{
						GameBase.gi.dprintf(self.classname + " at " + Lib.vtos(self.s.origin) + " has bad combattarget " + self.combattarget + " : " + target.classname + " at " + Lib.vtos(target.s.origin));
					}
				}
			}
			
			if (self.target != null)
			{
				self.goalentity = self.movetarget = GameBase.G_PickTarget(self.target);
				if (null == self.movetarget)
				{
					GameBase.gi.dprintf(self.classname + " can't find target " + self.target + " at " + Lib.vtos(self.s.origin) + "\n");
					self.target = null;
					self.monsterinfo.pausetime = 100000000;
					self.monsterinfo.stand.think(self);
				}
				else if (Lib.strcmp(self.movetarget.classname, "path_corner") == 0)
				{
					Math3D.VectorSubtract(self.goalentity.s.origin, self.s.origin, v);
					self.ideal_yaw = self.s.angles[Defines.YAW] = Math3D.vectoyaw(v);
					self.monsterinfo.walk.think(self);
					self.target = null;
				}
				else
				{
					self.goalentity = self.movetarget = null;
					self.monsterinfo.pausetime = 100000000;
					self.monsterinfo.stand.think(self);
				}
			}
			else
			{
				self.monsterinfo.pausetime = 100000000;
				self.monsterinfo.stand.think(self);
			}
			
			self.think = Monster.monster_think;
			self.nextthink = GameBase.level.time + Defines.FRAMETIME;
		}
		
		//UPGRADE_NOTE: The initialization of  'monster_think' was moved to static method 'jake2.game.Monster'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
		public static EntThinkAdapter monster_think;
		
		//UPGRADE_NOTE: The initialization of  'monster_triggered_spawn' was moved to static method 'jake2.game.Monster'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
		public static EntThinkAdapter monster_triggered_spawn;
		
		//	we have a one frame delay here so we don't telefrag the guy who activated
		// us
		//UPGRADE_NOTE: The initialization of  'monster_triggered_spawn_use' was moved to static method 'jake2.game.Monster'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
		public static EntUseAdapter monster_triggered_spawn_use;
		
		//UPGRADE_NOTE: The initialization of  'monster_triggered_start' was moved to static method 'jake2.game.Monster'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
		public static EntThinkAdapter monster_triggered_start;
		static Monster()
		{
			monster_think = new AnonymousClassEntThinkAdapter();
			monster_triggered_spawn = new AnonymousClassEntThinkAdapter1();
			monster_triggered_spawn_use = new AnonymousClassEntUseAdapter();
			monster_triggered_start = new AnonymousClassEntThinkAdapter2();
		}
	}
}